#include "Epoll.hpp"

void Epoll::create(){
    cout << "Init Epoll...." << endl;
    ep_fd = epoll_create(EPOLL_SIZE);
    if(ep_fd < 0){
        perror("epoll create error");
        exit(-1);
    }
}

void Epoll::add_listen_fd(int fd){
    struct epoll_event ev;
    ev.data.fd = fd;
    fcntl(fd, F_SETFL, fcntl(fd, F_GETFD, 0) | O_NONBLOCK);
    epoll_ctl(ep_fd, EPOLL_CTL_ADD, fd, &ev);
}

void Epoll::add_fd(HTTP_Request *_req, int fd, bool IsListenFd){
    struct epoll_event ev;
    ev.data.ptr = _req;                                  // 用事件的ptr指向这块儿内存，然后事件触发时就可以使用了
    ev.events = EPOLLIN | EPOLLET;
    if(IsListenFd == false){
        ev.events = EPOLLIN | EPOLLET | EPOLLONESHOT;          // 设置为边缘触发
        /*
            关于EPOLLONESHOT：
            在多线程处理时，一个SOCKET到来，唤醒一个线程来处理。
            但是如果此时该socketfd上又来了一个请求，会调用另外一个线程来处理
            这会诞生各种莫名奇妙的情况
            解决办法是设置为EPOLLONESHOT，这样第一次触发后，该sockefd上不会再触发了
            ，除非再次调用epoll_ctl注册，
        */
    }
     // 加入到epoll的监听事件
    epoll_ctl(ep_fd, EPOLL_CTL_ADD, fd, &ev);   
    // 将fd变为非阻塞IO，立即返回。
    fcntl(fd, F_SETFL, fcntl(fd, F_GETFD, 0) | O_NONBLOCK); 
}

void Epoll::mod_fd(HTTP_Request* _req){
    struct epoll_event event;
    event.data.ptr = (void *)_req;
    event.events = EPOLLIN | EPOLLET | EPOLLONESHOT;
    epoll_ctl(ep_fd, EPOLL_CTL_MOD, _req->get_client_fd(), &event);
}

void Epoll::wait_for_event(int timeout){
    event_num = epoll_wait(ep_fd, events, EPOLL_SIZE, timeout);
}

void Epoll::handle_event(Timer_Queue &timer_queue, int listen_fd, ThreadPool& threadpool, void (*func)(void*arg)){

    for (int i = 0; i < event_num; ++i)
    {
        HTTP_Request *request = (HTTP_Request *)(events[i].data.ptr);
        int socket_fd = request->get_client_fd();
        // 如果描述符等于监听描述符，说明是新的连接请求，accept并加入到EPOLL的监听事件当中
        if (socket_fd == listen_fd)
        {
            accept_connection(timer_queue, socket_fd);
        }
        // 如果描述符不等于监听描述符，说明是Http Request，加入到线程池处理。
        else{
            //cout << "Request From Client ID = " << socket_fd << endl;
            threadpool.add_job(func, (void*)(events[i].data.ptr));
        }
    }
}

void Epoll::accept_connection(Timer_Queue &timer_queue, int fd){
    struct sockaddr_in cilent_address;
    socklen_t client_addrLength = sizeof(struct sockaddr_in);
    int client_fd = accept(fd, (struct sockaddr *)&cilent_address, &client_addrLength);
    
    HTTP_Request *request = new HTTP_Request(client_fd, ep_fd);    // 这里就为请求分配好内存
    add_fd(request, client_fd, false);    // 把accept的client_fd加入到epoll监听事件当中
    timer_queue.add_timer(request);

    //cout << "New Client, Client_fd: " << client_fd << endl;
}